/*
 *                    COPYRIGHT NOTICE
 *
 *           This code is copyright protected by Freedom Inc.
 *
 *              2001-2012 All Rights Are Reserved
 */
package com.freedom.oldsong.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * @author steven.wang
 *
 */
public final class PrivilegeUtil {
	
//	private static final Logger logger = Logger.getLogger(PrivilegeUtil.class);
	
//	private static boolean isDevMode = true;

	public static final String SEPARATOR = ",";

	public static final String PRIVILEGE_PREFIX = "PR_";

	public static final String ROLE_PREFIX = "ROLE_";

	public static final String ORG_ID_PREFIX = "ORG_";
	
	public static final String SYS_ROLE_PREFIX = "SYS_";
	
	public static final String SYSTEM_GROUP = "SYSTEM";
	
	public static final String SYSTEM_OWNER = "SYSTEM_OWNER";
	
//	public static final String ANY_GROUP = "1";
	
	private PrivilegeUtil(){
		
	}
	
	public static Authentication getAuthentication() {		
		return SecurityContextHolder.getContext().getAuthentication();		
	}

	/**
	 * get current user name.
	 * @return
	 */
	public static String getUserName(Authentication authentication) {
		Authentication authen = (authentication == null) ? getAuthentication()
				: authentication;
		Object userDetail = authen.getPrincipal();
		String userName = "";
		if (userDetail instanceof UserDetails) {
			UserDetails detail = (UserDetails) userDetail;
			userName = detail.getUsername();
		} else {
			userName = userDetail.toString();
		}
		return userName;
	}

	/**
	 * get authorities(roles or privileges) from authentication.
	 * @param authentication
	 * @return
	 */
	public static List<String> getAuthorities(Authentication authentication) {
		Authentication authen = (authentication == null) ? getAuthentication()
				: authentication;
		List<String> authorities = new ArrayList<String>();
		Object userDetail = authen.getPrincipal();
		if (userDetail instanceof UserDetails) {
			UserDetails detail = (UserDetails) userDetail;
			Set<GrantedAuthority> grantedAuthorities = (Set<GrantedAuthority>)detail.getAuthorities();
			if (grantedAuthorities != null) {
				Iterator<GrantedAuthority> its=grantedAuthorities.iterator();
				while(its.hasNext()){
					GrantedAuthority it = its.next();
					authorities.add(it.getAuthority());
				}
			}
		}
		return authorities;
	}

	/**
	 * get current privileges.
	 * @return
	 */
	public static List<String> getPrivileges() {
		return getAuthoritiesByPrefix(PRIVILEGE_PREFIX);
	}

	/**
	 * get current roles.
	 * @return
	 */
	public static List<String> getRoles() {
		return getAuthoritiesByPrefix(ROLE_PREFIX);
	}

	/**
	 * Get all privileges with different project groups.
	 * 
	 * @return
	 */
	public static Map<String, List<String>> getGroupPrivileges() {
		List<String> groupPrivileges = getAuthoritiesByPrefix(ORG_ID_PREFIX);
		Map<String, List<String>> groupPrivilegeMap = new HashMap<String, List<String>>();
		if (groupPrivileges != null && groupPrivileges.size() > 0) {
			for (String gp : groupPrivileges) {
				int index = gp.indexOf(PRIVILEGE_PREFIX);
				if (index > -1) {
					String orgId = gp.substring(0, index).replaceAll(ORG_ID_PREFIX, "");
					List<String> privileges = groupPrivilegeMap.get(orgId);
					if (privileges == null) {
						privileges = new ArrayList<String>();
						groupPrivilegeMap.put(orgId, privileges);
					}
					privileges.add(gp.substring(index).replaceAll(PRIVILEGE_PREFIX, ""));
				}
			}
		}
		return groupPrivilegeMap;
	}
	
	/**
	 * Get all privileges with different project groups.
	 * 
	 * @return
	 */
	public static Map<String, List<String>> getGroupRoles() {
		List<String> groupPrivileges = getAuthoritiesByPrefix(ORG_ID_PREFIX);
		Map<String, List<String>> groupRolesMap = new HashMap<String, List<String>>();
		if (groupPrivileges != null && groupPrivileges.size() > 0) {
			for (String gp : groupPrivileges) {
				int index = gp.indexOf(ROLE_PREFIX);
				if (index > -1) {
					String orgId = gp.substring(0, index).replaceAll(ORG_ID_PREFIX, "");
					List<String> roles = groupRolesMap.get(orgId);
					if (roles == null) {
						roles = new ArrayList<String>();
						groupRolesMap.put(orgId, roles);
					}
					roles.add(gp.substring(index).replaceAll(ROLE_PREFIX, ""));
				}
			}
		}
		return groupRolesMap;
	}
	
	/**
	 * Get the corresponding group by privilege
	 * 
	 * @param privilege
	 * @return the corresponding group
	 */
	public static List<String> getOrgsByPrivilege(String privilege) {
		List<String> orgPrivileges = getAuthoritiesByPrefix(ORG_ID_PREFIX);		
		List<String> groups = new ArrayList<String>();
		if (orgPrivileges != null && orgPrivileges.size() > 0) {
			for (String gp : orgPrivileges) {
				int index = gp.indexOf(privilege);
				if (index > -1) {
					groups.add(gp.substring(0, gp.indexOf(PRIVILEGE_PREFIX)).replaceAll(ORG_ID_PREFIX, ""));
				}
			}
		}
		return groups;
	}
	
	/**
	 * Get the corresponding group by role
	 * 
	 * @param privilege
	 * @return the corresponding group
	 */
	public static List<String> getOrgsbyRole(String role) {
		List<String> orgPrivileges = getAuthoritiesByPrefix(ORG_ID_PREFIX);		
		List<String> groups = new ArrayList<String>();
		if (orgPrivileges != null && orgPrivileges.size() > 0) {
			for (String gp : orgPrivileges) {
				int index = gp.indexOf(role);
				if (index > -1) {
					groups.add(gp.substring(0, gp.indexOf(ROLE_PREFIX)).replaceAll(ORG_ID_PREFIX, ""));
				}
			}
		}
		return groups;
	}
	
	/**
	 * Check if the current user has required privilege under the group
	 * 
	 * @param group
	 * @param requiredPrivilege
	 * @return true if the current user has required privilege under the group
	 */
	public static boolean hasAnyGroupPrivilege(String group, String requiredPrivilege) {
		List<String> privileges = getPrivilegesByGroup(group);
		if(privileges != null && privileges.size() > 0 && requiredPrivilege != null) {
			String[] reqPris = requiredPrivilege.split(SEPARATOR);
			for(String reqP:reqPris){
				reqP=reqP.replace(PRIVILEGE_PREFIX, "");
				for(String pri:privileges){
					if(Pattern.matches(requiredPrivilege, pri)){
						return true;
					}
				}
			}
		}		
		return false;
	}
	
	/**
	 * Check if the current user has required privilege under the group
	 * 
	 * @param group
	 * @param requiredPrivilege
	 * @return true if the current user has required privilege under the group
	 */
	public static boolean hasAnyGroupRole(String group, String requiredRole) {
		List<String> roles = getRolesByGroup(group);
		if(roles != null && roles.size() > 0 && requiredRole != null) {
			String[] reqRoles = requiredRole.split(SEPARATOR);
			for(String reqR:reqRoles){
				reqR=reqR.replace(ROLE_PREFIX, "");
				for(String role:roles){
					if(Pattern.matches(requiredRole, role)){
						return true;
					}
				}
			}
		}
		return false;
	}
	
	/**
	 * Check if the current user has one of the required privileges under the group
	 * 
	 * @param group
	 * @param requiredPrivileges
	 * @return true if the current user has one of requiredPrivilege under the group
	 */
	public static boolean hasAnyGroupPrivilege(String group, String[] requiredPrivileges) {
		List<String> privileges = getPrivilegesByGroup(group);
		if(privileges != null && privileges.size() > 0 && requiredPrivileges != null) {
			for(String rp:  requiredPrivileges) {
				for(String p:privileges){
					if(Pattern.matches(rp, p)){
						return true;
					}
				}
			}
		}		
		return false;
	}
	
	/**
	 * Check if the current user has one of the required privileges under the group
	 * 
	 * @param group
	 * @param requiredPrivileges
	 * @return true if the current user has one of requiredPrivilege under the group
	 */
	public static boolean hasAnyGroupRole(String group, String[] requiredRoles) {
		List<String> roles = getRolesByGroup(group);
		if(roles != null && roles.size() > 0 && requiredRoles != null) {
			for(String rp:  requiredRoles) {
				for(String p:roles){
					if(Pattern.matches(rp, p)){
						return true;
					}
				}
			}
		}		
		return false;
	}
	
	public static boolean hasAnyGroupRoleAndPrivilege(String group,String requireRoleAndPrivilege){
		if(requireRoleAndPrivilege==null||requireRoleAndPrivilege.length()==0){
			return false;
		}
		String[] reqRolePrs = requireRoleAndPrivilege.split(SEPARATOR);
		for(String reqRolePr:reqRolePrs){
			if(reqRolePr.startsWith(ROLE_PREFIX)&&hasAnyGroupRole(group, reqRolePr)){
				return true;
			}
			if(reqRolePr.startsWith(PRIVILEGE_PREFIX)&&hasAnyGroupPrivilege(group, reqRolePr)){
				return true;
			}
		}
		return false;
	}
	

	/**
	 * Get privileges under the given group
	 * 
	 * @param group
	 * @return all privileges under the given group
	 */
	public static List<String> getPrivilegesByGroup(String group) {
		List<String> privis = new ArrayList<String>();
		if(getGroupPrivileges().get(group)!=null){
			privis.addAll(getGroupPrivileges().get(group));
		}
		return privis;
	}
	
	/**
	 * Get privileges under the given group
	 * 
	 * @param group
	 * @return all privileges under the given group
	 */
	public static List<String> getRolesByGroup(String group) {
		List<String> roles = new ArrayList<String>();
		if(getGroupRoles().get(group)!=null){
			roles.addAll(getGroupRoles().get(group));
		}
		return roles;
	}

	/**
	 * Get privileges under the given group
	 * 
	 * @param group
	 * @return all privileges under the given group
	 */
//	public static List<String> getPrivilegesByGroupWithPrefix(String group) {
//		List<String> privis = new ArrayList<String>();
//		if(getGroupPrivileges().get(group)!=null){
//			privis.addAll(getGroupPrivileges().get(group));
//		}
//		if(!group.equals(SYSTEM_GROUP)){
//			if(getGroupPrivileges().get(ANY_GROUP)!=null){
//				privis.addAll(getGroupPrivileges().get(ANY_GROUP));
//			}
//			for(int i = 0; i<privis.size(); i++){
//				if(!privis.get(i).startsWith(PRIVILEGE_PREFIX)){
//					privis.set(i, PRIVILEGE_PREFIX + privis.get(i));
//				}
//			}
//		}else{
//			for(int i = 0; i<privis.size(); i++){
//				if(!privis.get(i).startsWith(SYS_ROLE_PREFIX)){
//					privis.set(i, SYS_ROLE_PREFIX + privis.get(i));
//				}
//			}
//		}
//	
//		return privis;
//	}
	
	public static List<String> getRolesAndPrivileges() {
		List<String> authorities = getRoles();
		authorities.addAll(getPrivileges());
		return authorities;
	}

	/**
	 * get current privileges.
	 * @return
	 */
	private static List<String> getAuthoritiesByPrefix(String prefix) {
		List<String> currentAuthorities = getAuthorities(null);
		List<String> authorities = new ArrayList<String>();
		for (String authority : currentAuthorities) {
			if (authority.startsWith(prefix)) {
				authorities.add(authority);
			}
		}
		return authorities;
	}

	public static boolean hasAllPrivilege(String[] requiredPrivileges) {
		List<String> currentAuthorities = getAuthorities(null);
		return checkAllPrivilege(requiredPrivileges, currentAuthorities);
	}

	public static boolean hasAnyPrivilege(String[] requiredPrivileges) {
		List<String> currentAuthorities = getAuthorities(null);
		return checkAnyPrivilege(requiredPrivileges, currentAuthorities);
	}

	public static boolean hasPrivilege(String requiredPrivilege,
			List<String> currentAuthorities) {
		String[] requiredPrivileges = { requiredPrivilege };
		return checkAnyPrivilege(requiredPrivileges, currentAuthorities);
	}

	public static boolean hasAnyRole(String[] requiredRoles) {
		 List<String> currentAuthorities = getAuthoritiesByPrefix(ROLE_PREFIX);
		return checkAnyRole(requiredRoles, currentAuthorities);
	}
	
	/**
	 * check whether has one of the required privileges(role or privilege).
	 * @param requiredPrivileges
	 * @return
	 */
	public static boolean checkAnyPrivilege(String[] requiredPrivileges,
			List<String> currentAuthorities) {
		if(requiredPrivileges!=null&&currentAuthorities!=null){
			for(String reqP:requiredPrivileges){
				for(String cur:currentAuthorities){
					reqP=reqP.replace(PRIVILEGE_PREFIX, "");
					cur=cur.replace(PRIVILEGE_PREFIX, "");
					if(Pattern.matches(reqP, cur)){
						return true;
					}
				}
			}
		}
		return false;
	}
	
	
	/**
	 * check whether has one of the required privileges(role or privilege).
	 * @param requiredPrivileges
	 * @return
	 */
	public static boolean checkAnyRole(String[] requiredRoles,
			List<String> currentAuthorities) {
		if(requiredRoles!=null&&currentAuthorities!=null){
			for(String reqP:requiredRoles){
				for(String cur:currentAuthorities){
					reqP=reqP.replace(ROLE_PREFIX, "");
					cur=cur.replace(ROLE_PREFIX, "");
					if(Pattern.matches(reqP, cur)){
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * check whether has all of the required privileges(role or privilege).
	 * @param requiredPrivileges
	 * @return
	 */
	public static boolean checkAllPrivilege(String[] requiredPrivileges,
			List<String> currentAuthorities) {
		if(requiredPrivileges!=null&&currentAuthorities!=null){
			for(String reqP:requiredPrivileges){
				boolean flag=false;
				for(String cur:currentAuthorities){
					reqP=reqP.replace(PRIVILEGE_PREFIX, "");
					cur=cur.replace(PRIVILEGE_PREFIX, "");
					if(Pattern.matches(reqP, cur)){
						flag=true;
						break;
					}
				}
				if(!flag){
					return flag;
				}
			}
		}
		return false;
	}

}
